package bma.common.langutil.ai.common;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import bma.common.langutil.ai.AIUtil;
import bma.common.langutil.ai.stack.AIStack;
import bma.common.langutil.ai.vm.AIContext;
import bma.common.langutil.ai.vm.AIContextThread;
import bma.common.langutil.ai.vm.AIVM;
import bma.common.langutil.core.ObjectUtil;

/**
 * 并行执行门闩控制
 * 
 * @author guanzhong
 * @since 1.0 (imported from BMA.ESP Project)
 * 
 * @param <RESP_TYPE>
 */
public class AICountDownLatch<RESP_TYPE> {

	/**
	 * 控制次数
	 */
	protected int count;
	/**
	 * 每次只有一个通过
	 */
	protected boolean singlePass;

	protected transient int runtimeCount;
	protected transient List<AIRuntime<RESP_TYPE>> functions = new LinkedList<AIRuntime<RESP_TYPE>>();

	public AICountDownLatch(int count) {
		this(count, false);
	}

	public AICountDownLatch(int c, boolean singlePass) {
		super();
		this.singlePass = singlePass;
		if (c < 0)
			throw new IllegalArgumentException("count < 0");
		this.count = c;
		this.runtimeCount = c;
	}

	public int getLatchCount() {
		return count;
	}

	public int getCurrentCount() {
		return this.runtimeCount;
	}

	public boolean waitFor(AIFunction<RESP_TYPE> fun, AIStack<RESP_TYPE> stack) {
		AIRuntime<RESP_TYPE> item = AIRuntime.create(fun, stack);
		item = _push(item, 0);
		if (item == null) {
			return false;
		}
		return item.apply(null);
	}

	public boolean waitFor(AIFunction<RESP_TYPE> fun, AIStack<RESP_TYPE> stack,
			long timeout) {
		AIRuntime<RESP_TYPE> item = AIRuntime.create(fun, stack);
		item = _push(item, timeout);
		if (item == null) {
			return false;
		}
		return item.apply(null);
	}

	public void countDown() {
		countDown(1);
	}

	public void countDown(int v) {
		List<AIRuntime<RESP_TYPE>> itemList = this._countDown(v);
		if (itemList != null) {
			for (AIRuntime<RESP_TYPE> item : itemList) {
				item.run(null);
			}
		}
	}

	protected synchronized AIRuntime<RESP_TYPE> _push(
			AIRuntime<RESP_TYPE> item, long timeout) {
		if (this.runtimeCount == 0) {
			if (this.singlePass) {
				this.runtimeCount = this.count;
			}
			return item;
		} else {
			if (timeout > 0) {
				final Exception tme = new AITimeoutException(
						"AICountDownLatch.execute");
				AITimeoutTask<RESP_TYPE> tm = new AITimeoutTask<RESP_TYPE>(
						item.getStack(), AIContextThread.create(), timeout) {
					@Override
					protected void stackTimeout(AIContext ctx,
							AIStack<RESP_TYPE> stack) {
						AICountDownLatch.this.cancel(stack);
						AIUtil.executeFailure(ctx, stack, tme);
					}
				};
				AIVM.current().sureTimer().postTimerTask(tm);
			}
			this.functions.add(item);
			return null;
		}
	}

	protected synchronized List<AIRuntime<RESP_TYPE>> _countDown(int v) {
		if (this.runtimeCount >= v) {
			this.runtimeCount -= v;
		} else {
			this.runtimeCount = 0;
		}
		if (functions.isEmpty()) {
			return null;
		}
		if (this.runtimeCount == 0) {
			if (this.singlePass) {
				this.runtimeCount = this.count;
				ArrayList<AIRuntime<RESP_TYPE>> r = new ArrayList<AIRuntime<RESP_TYPE>>(
						1);
				r.add(this.functions.remove(0));
				return r;
			} else {
				ArrayList<AIRuntime<RESP_TYPE>> r = new ArrayList<AIRuntime<RESP_TYPE>>(
						this.functions);
				functions.clear();
				return r;
			}
		}
		return null;
	}

	public void cancel(AIStack<RESP_TYPE> stack) {
		synchronized (this) {
			Iterator<AIRuntime<RESP_TYPE>> it = this.functions.iterator();
			while (it.hasNext()) {
				AIRuntime<RESP_TYPE> rt = it.next();
				if (ObjectUtil.equals(rt.getStack(), stack)) {
					it.remove();
				}
			}
		}
	}

}
